home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / alpha.arc / FTPCLI.C < prev    next >
C/C++ Source or Header  |  1988-02-16  |  13KB  |  596 lines

  1. /* FTP client (interactive user) code */
  2. #include <stdio.h>
  3. #include "global.h"
  4. #include "mbuf.h"
  5. #include "netuser.h"
  6. #include "icmp.h"
  7. #include "timer.h"
  8. #include "tcp.h"
  9. #include "ftp.h"
  10. #include "session.h"
  11. #include "cmdparse.h"
  12.  
  13. extern struct session *current;
  14. extern char nospace[];
  15. extern char badhost[];
  16. static char notsess[] = "Not an FTP session!\n";
  17. static char cantwrite[] = "Can't write %s\n";
  18. static char cantread[] = "Can't read %s\n";
  19.  
  20. int donothing(),doftpcd(),dolist(),doget(),dols(),doput(),dotype(),doabort(),
  21.     domkdir(),dormdir();
  22.  
  23. struct cmds ftpabort[] = {
  24.     "",        donothing,    0,    NULLCHAR,        NULLCHAR,
  25.     "abort",    doabort,    0,    NULLCHAR,        NULLCHAR,
  26.     NULLCHAR,    NULLFP,        0,    "Only valid command is \"abort\"", NULLCHAR,
  27. };
  28.  
  29. struct cmds ftpcmds[] = {
  30.     "",        donothing,    0,    NULLCHAR,        NULLCHAR,
  31.     "cd",        doftpcd,    2,    "cd <directory>",    NULLCHAR,
  32.     "dir",        dolist,        0,    NULLCHAR,        NULLCHAR,
  33.     "list",        dolist,        0,    NULLCHAR,        NULLCHAR,
  34.     "get",        doget,        2,    "get remotefile <localfile>",    NULLCHAR,
  35.     "ls",        dols,        0,    NULLCHAR,        NULLCHAR,
  36.     "mkdir",    domkdir,    2,    "mkdir <directory>",    NULLCHAR,
  37.     "nlst",        dols,        0,    NULLCHAR,        NULLCHAR,
  38.     "rmdir",    dormdir,    2,    "rmdir <directory>",    NULLCHAR,
  39.     "put",        doput,        2,    "put localfile <remotefile>",    NULLCHAR,
  40.     "type",        dotype,        0,    NULLCHAR,        NULLCHAR,
  41.     NULLCHAR,    NULLFP,        0,     NULLCHAR,        NULLCHAR,
  42. };
  43.  
  44. /* Handle top-level FTP command */
  45. doftp(argc,argv)
  46. int argc;
  47. char *argv[];
  48. {
  49.     int32 resolve();
  50.     int ftpparse();
  51.     char *inet_ntoa();
  52.     void ftpccr(),ftpccs();
  53.     struct session *s;
  54.     struct ftp *ftp,*ftp_create();
  55.     struct tcb *tcb;
  56.     struct socket lsocket,fsocket;
  57.  
  58.     lsocket.address = ip_addr;
  59.     lsocket.port = lport++;
  60.     if((fsocket.address = resolve(argv[1])) == 0){
  61.         printf(badhost,argv[1]);
  62.         return 1;
  63.     }
  64.     if(argc < 3)
  65.         fsocket.port = FTP_PORT;
  66.     else
  67.         fsocket.port = atoi(argv[2]);
  68.  
  69.     /* Allocate a session control block */
  70.     if((s = newsession()) == NULLSESSION){
  71.         printf("Too many sessions\n");
  72.         return 1;
  73.     }
  74.     current = s;
  75.     if((s->name = malloc((unsigned)strlen(argv[1])+1)) != NULLCHAR)
  76.         strcpy(s->name,argv[1]);
  77.     s->type = FTP;
  78.     s->parse = ftpparse;
  79.  
  80.     /* Allocate an FTP control block */
  81.     if((ftp = ftp_create(0)) == NULLFTP){
  82.         s->type = FREE;
  83.         printf(nospace);
  84.         return 1;
  85.     }
  86.     ftp->state = COMMAND_STATE;
  87.     s->cb.ftp = ftp;    /* Downward link */
  88.     ftp->session = s;    /* Upward link */
  89.  
  90.     /* Now open the control connection */
  91.     tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,
  92.         0,ftpccr,NULLVFP,ftpccs,0,(char *)ftp);
  93.     ftp->control = tcb;
  94.     go();
  95.     return 0;
  96. }
  97. /* Parse user FTP commands */
  98. int
  99. ftpparse(line,len)
  100. char *line;
  101. int16 len;
  102. {
  103.     struct mbuf *bp;
  104.  
  105.     if(current->cb.ftp->state != COMMAND_STATE){
  106.         /* The only command allowed in data transfer state is ABORT */
  107.         if(cmdparse(ftpabort,line) == -1){
  108.             printf("Transfer in progress; only ABORT is acceptable\n");
  109.         }
  110.         fflush(stdout);
  111.         return;
  112.     }
  113.  
  114.     /* Save it now because cmdparse modifies the original */
  115.     bp = qdata(line,len);
  116.  
  117.     if(cmdparse(ftpcmds,line) == -1){
  118.         /* Send it direct */
  119.         if(bp != NULLBUF)
  120.             send_tcp(current->cb.ftp->control,bp);
  121.         else
  122.             printf(nospace);
  123.     } else {
  124.         free_p(bp);
  125.     }
  126.     fflush(stdout);
  127. }
  128. /* Handle null line to avoid trapping on first command in table */
  129. static
  130. int
  131. donothing(argc,argv)
  132. int argc;
  133. char *argv[];
  134. {
  135. }
  136. /* Translate 'cd' to 'cwd' for convenience */
  137. static
  138. int
  139. doftpcd(argc,argv)
  140. int argc;
  141. char *argv[];
  142. {
  143.     register struct ftp *ftp;
  144.  
  145.     ftp = current->cb.ftp;
  146.     return sndftpmsg(ftp,"CWD %s\r\n",argv[1]);
  147. }
  148. /* Translate 'mkdir' to 'xmkd' for convenience */
  149. static
  150. int
  151. domkdir(argc,argv)
  152. int argc;
  153. char *argv[];
  154. {
  155.     register struct ftp *ftp;
  156.  
  157.     ftp = current->cb.ftp;
  158.     return sndftpmsg(ftp,"XMKD %s\r\n",argv[1]);
  159. }
  160. /* Translate 'rmdir' to 'xrmd' for convenience */
  161. static
  162. int
  163. dormdir(argc,argv)
  164. int argc;
  165. char *argv[];
  166. {
  167.     register struct ftp *ftp;
  168.  
  169.     ftp = current->cb.ftp;
  170.     return sndftpmsg(ftp,"XRMD %s\r\n",argv[1]);
  171. }
  172. /* Handle "type" command from user */
  173. static
  174. int
  175. dotype(argc,argv)
  176. int argc;
  177. char *argv[];
  178. {
  179.     register struct ftp *ftp;
  180.  
  181.     ftp = current->cb.ftp;
  182.     if(argc < 2){
  183.         switch(ftp->type){
  184.         case IMAGE_TYPE:
  185.             printf("Image\n");
  186.             break;
  187.         case ASCII_TYPE:
  188.             printf("Ascii\n");
  189.             break;
  190.         }
  191.         return 0;
  192.     }
  193.     switch(*argv[1]){
  194.     case 'i':
  195.     case 'b':
  196.         ftp->type = IMAGE_TYPE;
  197.         sndftpmsg(ftp,"TYPE I\r\n");
  198.         break;
  199.     case 'a':
  200.         ftp->type = ASCII_TYPE;
  201.         sndftpmsg(ftp,"TYPE A\r\n");
  202.         break;
  203.     case 'l':
  204.         ftp->type = IMAGE_TYPE;
  205.         sndftpmsg(ftp,"TYPE L %s\r\n",argv[2]);
  206.         break;
  207.     default:
  208.         printf("Invalid type %s\n",argv[1]);
  209.         return 1;
  210.     }
  211.     return 0;
  212. }
  213. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  214. static
  215. doget(argc,argv)
  216. int argc;
  217. char *argv[];
  218. {
  219.     void ftpdr(),ftpcds();
  220.     char *remotename,*localname;
  221.     register struct ftp *ftp;
  222.     char *mode;
  223.  
  224.     ftp = current->cb.ftp;
  225.     if(ftp == NULLFTP){
  226.         printf(notsess);
  227.         return 1;
  228.     }
  229.     remotename = argv[1];
  230.     if(argc < 3)
  231.         localname = remotename;
  232.     else
  233.         localname = argv[2];
  234.  
  235.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  236.         fclose(ftp->fp);
  237.     ftp->fp = NULLFILE;
  238.  
  239.     if(ftp->type == IMAGE_TYPE)
  240.         mode = binmode[WRITE_BINARY];
  241.     else
  242.         mode = "w";
  243.  
  244.     if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  245.         printf(cantwrite,localname);
  246.         return 1;
  247.     }
  248.     ftp->state = RECEIVING_STATE;
  249.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  250.  
  251.     /* Generate the command to start the transfer */
  252.     return sndftpmsg(ftp,"RETR %s\r\n",remotename);
  253. }
  254. /* List remote directory. Syntax: dir <remote directory/file> [<local name>] */
  255. static
  256. dolist(argc,argv)
  257. int argc;
  258. char *argv[];
  259. {
  260.     void ftpdr(),ftpcds();
  261.     register struct ftp *ftp;
  262.  
  263.     ftp = current->cb.ftp;
  264.     if(ftp == NULLFTP){
  265.         printf(notsess);
  266.         return 1;
  267.     }
  268.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  269.         fclose(ftp->fp);
  270.     ftp->fp = NULLFILE;
  271.  
  272.     if(argc < 3){
  273.         ftp->fp = stdout;
  274.     } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  275.         printf(cantwrite,argv[2]);
  276.         return 1;
  277.     }
  278.     ftp->state = RECEIVING_STATE;
  279.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  280.     /* Generate the command to start the transfer
  281.      * It's done this way to avoid confusing the 4.2 FTP server
  282.      * if there's no argument
  283.      */
  284.     if(argc > 1)
  285.         return sndftpmsg(ftp,"LIST %s\r\n",argv[1]);
  286.     else
  287.         return sndftpmsg(ftp,"LIST\r\n","");
  288. }
  289. /* Abbreviated (name only) list of remote directory.
  290.  * Syntax: ls <remote directory/file> [<local name>]
  291.  */
  292. static
  293. dols(argc,argv)
  294. int argc;
  295. char *argv[];
  296. {
  297.     void ftpdr(),ftpcds();
  298.     register struct ftp *ftp;
  299.  
  300.     ftp = current->cb.ftp;
  301.     if(ftp == NULLFTP){
  302.         printf(notsess);
  303.         return 1;
  304.     }
  305.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  306.         fclose(ftp->fp);
  307.     ftp->fp = NULLFILE;
  308.  
  309.     if(argc < 3){
  310.         ftp->fp = stdout;
  311.     } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  312.         printf(cantwrite,argv[2]);
  313.         return 1;
  314.     }
  315.     ftp->state = RECEIVING_STATE;
  316.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  317.     /* Generate the command to start the transfer */
  318.     if(argc > 1)
  319.         return sndftpmsg(ftp,"NLST %s\r\n",argv[1]);
  320.     else
  321.         return sndftpmsg(ftp,"NLST\r\n","");
  322. }
  323. /* Start transmit. Syntax: put <local name> [<remote name>] */
  324. static
  325. doput(argc,argv)
  326. int argc;
  327. char *argv[];
  328. {
  329.     void ftpdt(),ftpcds();
  330.     char *remotename,*localname;
  331.     char *mode;
  332.     struct ftp *ftp;
  333.  
  334.     if((ftp = current->cb.ftp) == NULLFTP){
  335.         printf(notsess);
  336.         return 1;
  337.     }
  338.     localname = argv[1];
  339.     if(argc < 3)
  340.         remotename = localname;
  341.     else
  342.         remotename = argv[2];
  343.  
  344.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  345.         fclose(ftp->fp);
  346.  
  347.     if(ftp->type == IMAGE_TYPE)
  348.         mode = binmode[READ_BINARY];
  349.     else
  350.         mode = "r";
  351.  
  352.     if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  353.         printf(cantread,localname);
  354.         return 1;
  355.     }
  356.     ftp->state = SENDING_STATE;
  357.     ftpsetup(ftp,NULLVFP,ftpdt,ftpcds);
  358.  
  359.     /* Generate the command to start the transfer */
  360.     return sndftpmsg(ftp,"STOR %s\r\n",remotename);
  361. }
  362. /* Abort a GET or PUT operation in progress. Note: this will leave
  363.  * the partial file on the local or remote system
  364.  */
  365. doabort(argc,argv)
  366. int argc;
  367. char *argv[];
  368. {
  369.     register struct ftp *ftp;
  370.  
  371.     ftp = current->cb.ftp;
  372.  
  373.     /* Close the local file */
  374.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  375.         fclose(ftp->fp);
  376.     ftp->fp = NULLFILE;
  377.  
  378.     switch(ftp->state){
  379.     case SENDING_STATE:
  380.         /* Send a premature EOF.
  381.          * Unfortunately we can't just reset the connection
  382.          * since the remote side might end up waiting forever
  383.          * for us to send something.
  384.          */
  385.         close_tcp(ftp->data);
  386.         printf("Put aborted\n");
  387.         break;
  388.     case RECEIVING_STATE:
  389.         /* Just exterminate the data channel TCB; this will
  390.          * generate a RST on the next data packet which will
  391.          * abort the sender
  392.          */
  393.         del_tcp(ftp->data);
  394.         ftp->data = NULLTCB;
  395.         printf("Get aborted\n");
  396.         break;
  397.     }
  398.     ftp->state = COMMAND_STATE;
  399.     fflush(stdout);
  400. }
  401. /* create data port, and send PORT message */
  402. static
  403. ftpsetup(ftp,recv,send,state)
  404. struct ftp *ftp;
  405. void (*send)();
  406. void (*recv)();
  407. void (*state)();
  408. {
  409.     struct socket lsocket;
  410.     struct mbuf *bp;
  411.  
  412.     lsocket.address = ip_addr;
  413.     lsocket.port = lport++;
  414.  
  415.     /* Compose and send PORT a,a,a,a,p,p message */
  416.  
  417.     if((bp = alloc_mbuf(35)) == NULLBUF){    /* 5 more than worst case */
  418.         printf(nospace);
  419.         return;
  420.     }
  421.     /* I know, this looks gross, but it works! */
  422.     sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
  423.         hibyte(hiword(lsocket.address)),
  424.         lobyte(hiword(lsocket.address)),
  425.         hibyte(loword(lsocket.address)),
  426.         lobyte(loword(lsocket.address)),
  427.         hibyte(lsocket.port),
  428.         lobyte(lsocket.port));
  429.     bp->cnt = strlen(bp->data);
  430.     send_tcp(ftp->control,bp);
  431.  
  432.     /* Post a listen on the data connection */
  433.     ftp->data = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,
  434.         recv,send,state,0,(char *)ftp);
  435. }
  436. /* FTP Client Control channel Receiver upcall routine */
  437. void
  438. ftpccr(tcb,cnt)
  439. register struct tcb *tcb;
  440. int16 cnt;
  441. {
  442.     struct mbuf *bp;
  443.     struct ftp *ftp;
  444.  
  445.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  446.         /* Unknown connection; kill it */
  447.         close_tcp(tcb);
  448.         return;
  449.     }
  450.     /* Hold output if we're not the current session */
  451.     if(mode != CONV_MODE || current == NULLSESSION || current->cb.ftp != ftp)
  452.         return;
  453.  
  454.     if(recv_tcp(tcb,&bp,cnt) > 0){
  455.         while(bp != NULLBUF){
  456.             fwrite(bp->data,1,(unsigned)bp->cnt,stdout);
  457.             bp = free_mbuf(bp);
  458.         }
  459.         fflush(stdout);
  460.     }
  461. }
  462.  
  463. /* FTP Client Control channel State change upcall routine */
  464. static
  465. void
  466. ftpccs(tcb,old,new)
  467. register struct tcb *tcb;
  468. char old,new;
  469. {
  470.     void ftp_delete();
  471.     struct ftp *ftp;
  472.     char notify = 0;
  473.     extern char *tcpstates[];
  474.     extern char *reasons[];
  475.     extern char *unreach[];
  476.     extern char *exceed[];
  477.  
  478.     /* Can't add a check for unknown connection here, it would loop
  479.      * on a close upcall! We're just careful later on.
  480.      */
  481.     ftp = (struct ftp *)tcb->user;
  482.  
  483.     if(current != NULLSESSION && current->cb.ftp == ftp)
  484.         notify = 1;
  485.  
  486.     switch(new){
  487.     case CLOSE_WAIT:
  488.         if(notify)
  489.             printf("%s\n",tcpstates[new]);
  490.         close_tcp(tcb);
  491.         break;
  492.     case CLOSED:    /* heh heh */
  493.         if(notify){
  494.             printf("%s (%s",tcpstates[new],reasons[tcb->reason]);
  495.             if(tcb->reason == NETWORK){
  496.                 switch(tcb->type){
  497.                 case DEST_UNREACH:
  498.                     printf(": %s unreachable",unreach[tcb->code]);
  499.                     break;
  500.                 case TIME_EXCEED:
  501.                     printf(": %s time exceeded",exceed[tcb->code]);
  502.                     break;
  503.                 }
  504.             }
  505.             printf(")\n");
  506.             cmdmode();
  507.         }
  508.         del_tcp(tcb);
  509.         if(ftp != NULLFTP)
  510.             ftp_delete(ftp);
  511.         break;
  512.     default:
  513.         if(notify)
  514.             printf("%s\n",tcpstates[new]);
  515.         break;
  516.     }
  517.     if(notify)
  518.         fflush(stdout);
  519. }
  520. /* FTP Client Data channel State change upcall handler */
  521. static
  522. void
  523. ftpcds(tcb,old,new)
  524. struct tcb *tcb;
  525. char old,new;
  526. {
  527.     struct ftp *ftp;
  528.  
  529.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  530.         /* Unknown connection, kill it */
  531.         close_tcp(tcb);
  532.         return;
  533.     }
  534.     switch(new){
  535.     case FINWAIT2:
  536.     case TIME_WAIT:
  537.         if(ftp->state == SENDING_STATE){
  538.             /* We've received an ack of our FIN, so
  539.              * return to command mode
  540.              */
  541.             ftp->state = COMMAND_STATE;
  542.             if(current != NULLSESSION && current->cb.ftp == ftp){
  543.                 printf("Put complete, %lu bytes sent\n",
  544.                     tcb->snd.una - tcb->iss - 2);
  545.                 fflush(stdout);
  546.             }
  547.         }
  548.         break;        
  549.     case CLOSE_WAIT:
  550.         close_tcp(tcb);
  551.         if(ftp->state == RECEIVING_STATE){
  552.             /* End of file received on incoming file */
  553. #ifdef    CPM
  554.             if(ftp->type == ASCII_TYPE)
  555.                 putc(CTLZ,ftp->fp);
  556. #endif
  557.             if(ftp->fp != stdout)
  558.                 fclose(ftp->fp);
  559.             ftp->fp = NULLFILE;
  560.             ftp->state = COMMAND_STATE;
  561.             if(current != NULLSESSION && current->cb.ftp == ftp){
  562.                 printf("Get complete, %lu bytes received\n",
  563.                     tcb->rcv.nxt - tcb->irs - 2);
  564.                 fflush(stdout);
  565.             }
  566.         }
  567.         break;
  568.     case CLOSED:
  569.         ftp->data = NULLTCB;
  570.         del_tcp(tcb);
  571.         break;
  572.     }
  573. }
  574. /* Send a message on the control channel */
  575. /*VARARGS*/
  576. static
  577. int
  578. sndftpmsg(ftp,fmt,arg)
  579. struct ftp *ftp;
  580. char *fmt;
  581. char *arg;
  582. {
  583.     struct mbuf *bp;
  584.     int16 len;
  585.  
  586.     len = strlen(fmt) + strlen(arg) + 10;    /* fudge factor */
  587.     if((bp = alloc_mbuf(len)) == NULLBUF){
  588.         printf(nospace);
  589.         return 1;
  590.     }
  591.     sprintf(bp->data,fmt,arg);
  592.     bp->cnt = strlen(bp->data);
  593.     send_tcp(ftp->control,bp);
  594.     return 0;
  595. }
  596.